package com.ming.common.liteflow.core.flow.logicflow;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.ming.common.liteflow.core.el.FlowConvertELUtil;
import com.ming.common.liteflow.core.el.NodeInfoToELUtil;
import com.ming.common.liteflow.core.el.bus.*;
import com.ming.common.liteflow.core.execption.LiteFlowELException;
import com.ming.common.liteflow.core.graph.EdgeProperties;
import com.ming.common.liteflow.core.graph.Node;
import com.ming.common.liteflow.core.node.NodeInfoWrapper;
import com.yomahub.liteflow.builder.el.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;

public class LogicflowExecutor {

    public static ELWrapper elWrapper(LogicFlowGraphEL graphEL) throws LiteFlowELException {
        ELWrapper elWrapper = ELBus.then();
        //单起点
        if(graphEL.isSingeStart()){
            parseSingleFlow(elWrapper, graphEL, graphEL.getStartNode(), null);
        }else {//多起点
            parseMultipleFlow(elWrapper, graphEL, null, null);
        }
        return elWrapper;
    }

    //单起点解析
    public static void parseSingleFlow(ELWrapper elWrapper, LogicFlowGraphEL graphEL, Node startNode, Node endNode) throws LiteFlowELException {
        boolean isGroupNodeAndThenELWrapper = graphEL.isGroupNodeAndThenELWrapper(startNode,elWrapper);
        if(isGroupNodeAndThenELWrapper){
            WhenELWrapper whenELWrapper = ELBus.when();
            ThenELWrapper thenELWrapper = ELBus.then();
            parseSwitchCaseFlow(thenELWrapper, graphEL, startNode, endNode);
            whenELWrapper.when(thenELWrapper);
            graphEL.setGroupNodeProp(startNode,whenELWrapper);
            FlowConvertELUtil.convert(elWrapper,whenELWrapper);
        }else{
            parseSwitchCaseFlow(elWrapper, graphEL, startNode, endNode);
        }
    }

    public static void parseSwitchCaseFlow(ELWrapper elWrapper, LogicFlowGraphEL graphEL, Node startNode, Node endNode) throws LiteFlowELException {
        switch (startNode.getProperties().getType()){
            //选择组件处理
            case "switch":
            case "switch_script":
                flowSwitch(elWrapper, startNode, endNode, graphEL);
                break;
            case "if":
            case "if_script":
                flowIf(elWrapper, startNode, endNode, graphEL);
                break;
            case "for":
            case "for_script":
                flowFor(elWrapper, startNode, endNode, graphEL);
                break;
            case "while":
            case "while_script":
                flowWhile(elWrapper, startNode, endNode, graphEL);
                break;
            case "iterator":
                flowIterator(elWrapper, startNode, endNode, graphEL);
                break;
            default:
                if(graphEL.isFork(startNode)){
                    flowFork(elWrapper, startNode, endNode, graphEL);return;
                }else if(graphEL.isJoin(startNode)){
                    flowThen(elWrapper, startNode);
                }else{
                    flowThen(elWrapper, startNode);
                }
                if(!graphEL.isLastNode(startNode)){
                    Node nextNode = graphEL.getNextNode(startNode).get(0);
                    if(endNode != null && endNode == nextNode){ return; }
                    parseSingleFlow(elWrapper, graphEL, nextNode, endNode);
                }
                break;
        }
    }

        //多起点解析
    public static void parseMultipleFlow(ELWrapper wrapper, LogicFlowGraphEL graphEL, List<Node> startNodeList, Node endNode) throws LiteFlowELException {
        if(CollUtil.isEmpty(startNodeList)){
            startNodeList = graphEL.getStartNodeList();
        }
        Node joinNode = graphEL.getJoinNode(startNodeList);
        flowMultiple(wrapper,startNodeList,joinNode,graphEL);
        if(joinNode != null){
            parseSingleFlow(wrapper, graphEL, joinNode, endNode);
        }
    }

    // switch处理
    public static void flowSwitch(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> switchToList = new ArrayList<>();
        List<Node> switchDefaultList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList){
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if(flag1){ commonList.add(node); }
            boolean flag2 = graphEL.isSwitchToLine(startNode, node);
            if(flag2){ switchToList.add(node); }
            boolean flag3 = graphEL.isSwitchDefaultLine(startNode, node);
            if(flag3){ switchDefaultList.add(node); }
        }
        if(switchDefaultList.size() > 1){
            throw new LiteFlowELException("switch组件不能有多个default节点！");
        } else if (switchDefaultList.size() == 1 && switchToList.isEmpty()) {
            throw new LiteFlowELException("switch组件未设置switch to节点！");
        }
        List<ThenELWrapper> switchToELList = new ArrayList<>();
        List<ThenELWrapper> switchDefaultELList = new ArrayList<>();
        if(CollUtil.isNotEmpty(switchToList)){
            for (Node node : switchToList){
                ThenELWrapper thenELWrapper = ELBus.then();
                EdgeProperties edgeProperties = graphEL.getEdgeProp(startNode, node);
                if(edgeProperties != null) {
                    if (StrUtil.isNotBlank(edgeProperties.getId())) {
                        thenELWrapper.id(edgeProperties.getId());
                    }
                    if (StrUtil.isNotBlank(edgeProperties.getTag())) {
                        thenELWrapper.tag(edgeProperties.getTag());
                    }
                }
                parseSingleFlow(thenELWrapper, graphEL, node, null);
                switchToELList.add(thenELWrapper);
            }
        }
        if(CollUtil.isNotEmpty(switchDefaultList)){
            ThenELWrapper thenELWrapper = ELBus.then();
            parseSingleFlow(thenELWrapper, graphEL, switchDefaultList.get(0), null);
            switchDefaultELList.add(thenELWrapper);
        }

        if(CollUtil.isNotEmpty(switchToELList) || CollUtil.isNotEmpty(switchDefaultELList)){
            NodeInfoWrapper properties = startNode.getProperties();
            if(CollUtil.isNotEmpty(switchDefaultELList)){
                properties.setCmpDefaultOptEL(switchDefaultELList.get(0));
            }
            properties.setCmpToEL(switchToELList);
            ELWrapper elWrapper = ELBusSwitch.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper,elWrapper);
        }else{
            flowThen(wrapper, startNode);
        }
        if(commonList.size() == 1){//单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        }else if(commonList.size() > 1){//多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowIf(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> ifTrueList = new ArrayList<>();
        List<Node> ifFalseList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList){
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if(flag1){ commonList.add(node); }
            boolean flag2 = graphEL.isIfTrueLine(startNode, node);
            if(flag2){ ifTrueList.add(node); }
            boolean flag3 = graphEL.isIfFalseLine(startNode, node);
            if(flag3){ ifFalseList.add(node); }
        }
        if(ifTrueList.size() > 1){
            throw new LiteFlowELException("if组件不能有多个true节点！");
        } else if (ifFalseList.size() > 1) {
            throw new LiteFlowELException("if组件不能有多个false节点！");
        }
        ThenELWrapper trueELWrapper = null;
        ThenELWrapper falseELWrapper = null;
        if(CollUtil.isNotEmpty(ifTrueList)){
            trueELWrapper = ELBus.then();
            parseSingleFlow(trueELWrapper, graphEL, ifTrueList.get(0), null);
        }
        if(CollUtil.isNotEmpty(ifFalseList)){
            falseELWrapper = ELBus.then();
            parseSingleFlow(falseELWrapper, graphEL, ifFalseList.get(0), null);
        }

        if(trueELWrapper != null || falseELWrapper != null){
            NodeInfoWrapper properties = startNode.getProperties();
            if(trueELWrapper != null){
                properties.setCmpTrueOptEL(trueELWrapper);
            }
            if(falseELWrapper != null) {
                properties.setCmpFalseOptEL(falseELWrapper);
            }
            ELWrapper elWrapper = ELBusIf.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper,elWrapper);
        }else{
            flowThen(wrapper, startNode);
        }
        if(commonList.size() == 1){//单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        }else if(commonList.size() > 1){//多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowFor(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> doList = new ArrayList<>();
        List<Node> breakList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList){
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if(flag1){ commonList.add(node); }
            boolean flag2 = graphEL.isForLine(startNode, node);
            if(flag2){ doList.add(node); }
            boolean flag3 = graphEL.isBreakLine(startNode, node);
            if(flag3){ breakList.add(node); }
        }
        if(doList.size() > 1){
            throw new LiteFlowELException("for组件不能有多个do路径！");
        } else if (breakList.size() > 1) {
            throw new LiteFlowELException("for组件不能有多个break路径！");
        }
        ThenELWrapper doELWrapper = null;
        ELWrapper breakELWrapper = null;
        if(CollUtil.isNotEmpty(doList)){
            doELWrapper = ELBus.then();
            parseSingleFlow(doELWrapper, graphEL, doList.get(0), null);
        }
        if(CollUtil.isNotEmpty(breakList)){
//            breakELWrapper = ELBus.then();
//            parseSingleFlow(breakELWrapper, graphEL, breakList.get(0), null);
            breakELWrapper = ELBusBreak.NEW().nodeEL().node(breakList.get(0).getProperties()).toELWrapper();
        }

        if(doELWrapper != null || breakELWrapper != null){
            NodeInfoWrapper properties = startNode.getProperties();
            if(doELWrapper != null){
                properties.setCmpDoOptEL(doELWrapper);
            }
            if(breakELWrapper != null) {
                properties.setCmpBreakOptEL(breakELWrapper);
            }
            ELWrapper elWrapper = ELBusFor.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper,elWrapper);
        }else{
            flowThen(wrapper, startNode);
        }
        if(commonList.size() == 1){//单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        }else if(commonList.size() > 1){//多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowWhile(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> doList = new ArrayList<>();
        List<Node> breakList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList){
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if(flag1){ commonList.add(node); }
            boolean flag2 = graphEL.isWhileLine(startNode, node);
            if(flag2){ doList.add(node); }
            boolean flag3 = graphEL.isBreakLine(startNode, node);
            if(flag3){ breakList.add(node); }
        }
        if(doList.size() > 1){
            throw new LiteFlowELException("while组件不能有多个do路径！");
        } else if (breakList.size() > 1) {
            throw new LiteFlowELException("while组件不能有多个break路径！");
        }
        ThenELWrapper doELWrapper = null;
        ELWrapper breakELWrapper = null;
        if(CollUtil.isNotEmpty(doList)){
            doELWrapper = ELBus.then();
            parseSingleFlow(doELWrapper, graphEL, doList.get(0), null);
        }
        if(CollUtil.isNotEmpty(breakList)){
            //breakELWrapper = ELBus.then();
            //parseSingleFlow(breakELWrapper, graphEL, breakList.get(0), null);
            breakELWrapper = ELBusBreak.NEW().nodeEL().node(breakList.get(0).getProperties()).toELWrapper();
        }

        if(doELWrapper != null || breakELWrapper != null){
            NodeInfoWrapper properties = startNode.getProperties();
            if(doELWrapper != null){
                properties.setCmpDoOptEL(doELWrapper);
            }
            if(breakELWrapper != null) {
                properties.setCmpBreakOptEL(breakELWrapper);
            }
            ELWrapper elWrapper = ELBusWhile.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper,elWrapper);
        }else{
            flowThen(wrapper, startNode);
        }
        if(commonList.size() == 1){//单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        }else if(commonList.size() > 1){//多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    public static void flowIterator(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nodeList = graphEL.getNextNode(startNode);
        List<Node> doList = new ArrayList<>();
        List<Node> commonList = new ArrayList<>();
        for (Node node : nodeList){
            boolean flag1 = graphEL.isCommonLine(startNode, node);
            if(flag1){ commonList.add(node); }
            boolean flag2 = graphEL.isIteratorLine(startNode, node);
            if(flag2){ doList.add(node); }
        }
        if(doList.size() > 1){
            throw new LiteFlowELException("iterator组件不能有多个do路径！");
        }
        ThenELWrapper doELWrapper = null;
        if(CollUtil.isNotEmpty(doList)){
            doELWrapper = ELBus.then();
            parseSingleFlow(doELWrapper, graphEL, doList.get(0), null);
        }

        if(doELWrapper != null){
            NodeInfoWrapper properties = startNode.getProperties();
            properties.setCmpDoOptEL(doELWrapper);
            ELWrapper elWrapper = ELBusIterator.NEW().node(properties).toELWrapper();
            FlowConvertELUtil.convert(wrapper,elWrapper);
        }else{
            flowThen(wrapper, startNode);
        }
        if(commonList.size() == 1){//单起点
            parseSingleFlow(wrapper, graphEL, commonList.get(0), endNode);
        }else if(commonList.size() > 1){//多起点
            parseMultipleFlow(wrapper, graphEL, commonList, endNode);
        }
    }

    // 分叉节点处理
    public static void flowFork(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        flowThen(wrapper, startNode);
        Node joinNode = graphEL.getJoinNode(startNode);
        if(joinNode != null && graphEL.isMultipleJoin(startNode, joinNode)){//路径聚合
            parseMultipleFlow(wrapper, graphEL, graphEL.getNextNode(startNode), joinNode);
        }else if(joinNode == null && graphEL.isXNode(graphEL.getNextNode(startNode), null)){
            Set<List<Node>> startNodeGroupList = graphEL.isMultipleStartAndXNode(startNode, endNode);
            if(CollUtil.isNotEmpty(startNodeGroupList)){
                WhenELWrapper whenELWrapper = ELBus.when();
                for (List<Node> nodeList : startNodeGroupList){
                    if(nodeList.size() == 1){
                        ThenELWrapper thenELWrapper = ELBus.then();
                        parseSingleFlow(thenELWrapper, graphEL, nodeList.get(0), endNode);
                        whenELWrapper.when(thenELWrapper);
                    }else{
                        ThenELWrapper thenELWrapper = ELBus.then();
                        parseMultipleFlow(thenELWrapper, graphEL, nodeList, endNode);
                        whenELWrapper.when(thenELWrapper);
                    }
                }
                graphEL.setGroupNodeProp(startNode,whenELWrapper);
                FlowConvertELUtil.convert(wrapper,whenELWrapper);
            }else{
                flowWhen(wrapper,startNode,joinNode,graphEL);
                if (joinNode != null && joinNode != endNode) {
                    parseSingleFlow(wrapper, graphEL, joinNode, endNode);
                }
            }
        }else{
            flowWhen(wrapper,startNode,joinNode,graphEL);
            if (joinNode != null && joinNode != endNode) {
                parseSingleFlow(wrapper, graphEL, joinNode, endNode);
            }
        }
    }

    public static void flowMultiple(ELWrapper wrapper, List<Node> startNodeList, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        WhenELWrapper whenELWrapper = ELBus.when();
        boolean flag = graphEL.isXNode(startNodeList, endNode);
        if(flag){
            Set<List<Node>> startNodeGroupList = graphEL.startNodeGroupList(startNodeList, endNode);
            for (List<Node> nodeList : startNodeGroupList){
                if(nodeList.size() == 1){
                    ThenELWrapper thenELWrapper = ELBus.then();
                    parseSingleFlow(thenELWrapper, graphEL, nodeList.get(0), endNode);
                    whenELWrapper.when(thenELWrapper);
                }else{
                    ThenELWrapper thenELWrapper = ELBus.then();
                    parseMultipleFlow(thenELWrapper, graphEL, nodeList, endNode);
                    whenELWrapper.when(thenELWrapper);
                }
            }
        }else{
            for (Node startNode : startNodeList){
                if(graphEL.getNextNode(startNode).contains(endNode)){
                    whenELWrapper.when(nodeToEL(startNode));
                }else{
                    ThenELWrapper thenELWrapper = ELBus.then();
                    parseSingleFlow(thenELWrapper, graphEL, startNode, endNode);
                    whenELWrapper.when(thenELWrapper);
                }
            }
        }
        graphEL.setGroupNodeProp(startNodeList,whenELWrapper);
        FlowConvertELUtil.convert(wrapper,whenELWrapper);
    }

    public static void flowWhen(ELWrapper wrapper, Node startNode, Node endNode, LogicFlowGraphEL graphEL) throws LiteFlowELException {
        List<Node> nextNodeList = graphEL.getNextNode(startNode);
        WhenELWrapper whenELWrapper = ELBus.when();
        for (Node nextNode : nextNodeList){
            if(graphEL.isLastNode(nextNode) || graphEL.getNextNode(nextNode).contains(endNode)){
                whenELWrapper.when(nodeToEL(nextNode));
            }else{
                ThenELWrapper thenELWrapper = ELBus.then();
                parseSingleFlow(thenELWrapper, graphEL, nextNode, endNode);
                whenELWrapper.when(thenELWrapper);
            }
        }
        graphEL.setGroupNodeProp(startNode,whenELWrapper);
        FlowConvertELUtil.convert(wrapper,whenELWrapper);
    }

    // 串行处理
    public static void flowThen(ELWrapper wrapper, Node startNode) throws LiteFlowELException {
        FlowConvertELUtil.convert(wrapper,nodeToEL(startNode));
    }

    // 前置组件处理
    public static void preELWrapper(ThenELWrapper thenELWrapper,LogicFlowGraphEL graphEL) {
        Object[] preArray = nodeToEL(graphEL.getPreList());
        if(preArray != null){
            thenELWrapper.pre(preArray);
        }
    }

    // 后置组件处理
    public static void finallyELWrapper(ThenELWrapper thenELWrapper,LogicFlowGraphEL graphEL) {
        Object[] finallyArray = nodeToEL(graphEL.getFinallyList());
        if(finallyArray != null) {
            thenELWrapper.finallyOpt(finallyArray);
        }
    }


    public static Object nodeToEL(Node node) throws LiteFlowELException {
        if(node == null ){ return null; }
        return getELWrapper(node);
    }

    public static Object[] nodeToEL(List<Node> nodeList) {
        if(nodeList == null ){ return null; }
        return nodeList.stream().map(m -> {
            try {
                return getELWrapper(m);
            } catch (LiteFlowELException e) {
                throw new RuntimeException(e);
            }
        }).filter(Objects::nonNull).toArray();
    }

    public static Object getELWrapper(Node node) throws LiteFlowELException {
        NodeInfoWrapper nodeInfoWrapper = node.getProperties();
        return NodeInfoToELUtil.buildELWrapper(nodeInfoWrapper,true);
    }

}
